package org.sdnstproject.interlayer.DummyProxyV10;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import org.sdnstproject.interlayer.interlayerProxy.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OFFuzzerV10 {
    private static final Logger log = LoggerFactory.getLogger(OFFuzzerV10.class);
    private Configuration config = Configuration.getInstance();
    private AgentManager manager;
    public volatile boolean running = true;
    private ResultAnalyzer analyzer = ResultAnalyzer.getInstance();
    private DummySwitchV10 ofSwitch;
    private DummyControllerV10 ofController;
    private ListenV10 listen;
    private long start = 0;
    private long end = 0;

    public OFFuzzerV10() {
        this.manager = new AgentManager();
    }

    public String generateFlow(String proto) throws Exception{
        manager.hostmWriter(proto);
        String result = manager.hostmReader();
        if(result == null) log.error("generate flow and get result failed.");
        return result;
    }

    public void doMoreFuzzer() {
        String[] stras = new String[] {
//			"ofVersion:1.3,ofType:FEATURES_REPLY,childType:*,testWhich:controller,action:MODIFY,field:auxiliaryId,actionValue:32000",
                "ofVersion:1.3,ofType:FLOW_MOD,childType:*,testWhich:switch,action:MODIFY,field:hardTimeout,actionValue:2147483647"
        };
        FileWriter fileWriter = null;
        try {
            manager.start();
//			while(manager.readyAgent.get() < 2) {
//				Thread.sleep(500);
//			}
            synchronized (manager) {
                if(manager.readyAgent.get() < 2) {
                    log.info("waitting for 2 agents start...");
                    manager.wait();
                }
            }
            fileWriter = new FileWriter(config.getInterlayer_ROOT()+"/logs/fuzzresult.log",false);
            fileWriter.write("==================================================================="
                    + "\n#Fuzzing test result:\n");
            for(String stra:stras) {
                start = System.currentTimeMillis();
                String ret = doFuzzing(StrategesGen.getStrategyObject(stra));
                end = System.currentTimeMillis();
                log.info(ret);
                log.info(analyzer.toString());
                log.info("Running Time(s) : " + (end - start) / 1000.0);
                log.info("-----------------------------------------------------------------------------");
                fileWriter.write(ret+" ["+stra+"]\n");
                fileWriter.flush();
                if(ret == "FAIL") analyzer.printResultsToTerminnal(ret, stra);
            }
            //stop 2 agent in VM
            manager.getControllerm().doControl("stop agent");
            manager.getSwitchm().doControl("stop agent");
            Thread.sleep(500);
            manager.running = false;
            manager.interrupt();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                fileWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean doOpenFlowFuzzer() {
        String ret = null;
        FileWriter fileWriter =null;
        try {
            manager.start();
//			while(manager.readyAgent.get() < 2) {
//				Thread.sleep(500);
//			}
            synchronized (manager) {
                if(manager.readyAgent.get() < 2) {
                    log.info("waitting for 2 agents start...");
                    manager.wait();
                }
            }
            fileWriter = new FileWriter(config.getInterlayer_ROOT()+"/output/logs/fuzzresult.log",false);
            fileWriter.write("==================================================================="
                    + "\n#Fuzzing test result:\n");
            ArrayList<String> strategies = StrategesGen.readFromFile(config.getInterlayer_ROOT()+"/pyscript/strategiesV10.txt");
            for(String s:strategies) {
                Strategy strategy = StrategesGen.getStrategyObject(s);
                if(strategy == null) {
                    log.info("get strategy failed. strategy: "+s);
                    continue;
                }
                start = System.currentTimeMillis();
                ret = doFuzzing(strategy);
                end = System.currentTimeMillis();
                log.info(ret);
                log.info(analyzer.toString());
                log.info("Running Time(s) : " + (end - start) / 1000.0);
                log.info("-----------------------------------------------------------------------------");
                fileWriter.write(ret+" ["+s+"]\n");
                fileWriter.flush();
                if(ret == "FAIL") analyzer.printResultsToTerminnal(ret, s);
            }
            //stop 2 agent in VM
            manager.getControllerm().doControl("stop agent");
            manager.getSwitchm().doControl("stop agent");
            Thread.sleep(1000);
            manager.running = false;
            manager.interrupt();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fileWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    public String doFuzzing(Strategy strategy) {
        String ret = "UNKOWN";
        try {
            if(strategy.testWhich.equals("controller")) {
                log.info("begin to fuzzing test controller, strategy = " + strategy.toString());
                runController();
                log.info("waitting controller start...");
                String line = manager.getControllerm().getIn().readLine();
                if(line.contains("start")) {
                    ofSwitch = new DummySwitchV10(strategy);
                    analyzer.init();
                    synchronized (ofSwitch) {
                        ofSwitch.start();
                        ofSwitch.wait(10000);//10sec
                    }
                    if(ofSwitch.strategySend) {
                        //result analysis
                        Thread.sleep(3000);//this line could be delete if added waiting response notify for other msg
                        analyzer.isSocketClose = ofSwitch.socket.isClosed();
                        ret = analyzer.getResult();
                    }else {
                        log.info("Test over 10 seconds, test timed out");
                    }
                    ofSwitch.running = false;
                    ofSwitch.interrupt();
                    stopController();
                    Thread.sleep(500);
                }else {
                    log.info("controller start failed.");
                }
            }else {//test switch
                log.info("begin to fuzzing test switch, strategy = " + strategy.toString());
                analyzer.init();
                ofController = new DummyControllerV10(strategy);
                listen = new ListenV10(ofController);
                manager.getSwitchm().doControl("run mininet");
//    			ofController.listeningSwitch();
                synchronized (ofController) {
                    listen.start();
                    ofController.wait(10000);//10sec
                }
                if(ofController.strategySend) {
                    //result analysis
                    Thread.sleep(3000);//this line could be delete if added waiting response notify for other msg
                    analyzer.isSocketClose = ofController.targetSock.isClosed();
                    ret = analyzer.getResult();
                }else {
                    log.info("Test over 10 seconds, test timed out");
                }
                manager.getSwitchm().doControl("stop mininet");
                ofController.running = false;
                ofController.interrupt();
                listen.running = false;
                listen.interrupt();
                log.info("listen.isInterrupted:"+listen.isInterrupted()
                        +",ofController.isInterrupted:"+ofController.isInterrupted());
                log.info("listen.serverSock.isClosed:"+listen.serverSock.isClosed()
                        +",ofController.targetSock.isClosed:"+ofController.targetSock.isClosed());
                if(!listen.serverSock.isClosed()) listen.serverSock.close();
                if(!ofController.targetSock.isClosed()) ofController.targetSock.close();
                log.info("listen.serverSock.isClosed:"+listen.serverSock.isClosed()
                        +",ofController.targetSock.isClosed:"+ofController.targetSock.isClosed());
//				while(!listen.serverSock.isClosed()) {
//					listen.serverSock.close();
//					Thread.sleep(10);
//				}
//				while(!ofController.targetSock.isClosed()) {
//					ofController.targetSock.close();
//					Thread.sleep(10);
//				}
                Thread.sleep(500);
            }
        } catch (Exception e) {
            manager.getSwitchm().doControl("stop mininet");
            stopController();
            e.printStackTrace();
        }
        return ret;
    }

    public void runController() {
        switch (config.getTargetController().toLowerCase()) {
            case "ryu":
                manager.getControllerm().doControl("run ryu");
                break;
            case "floodlight":
                manager.getControllerm().doControl("run floodlight");
                break;
            case "odl":
                manager.getControllerm().doControl("run odl");
                break;
            case "onos":
                manager.getControllerm().doControl("run onos");
                break;
            case "pox":
                manager.getControllerm().doControl("run pox");
                break;
        }
    }

    public void stopController() {
        switch (config.getTargetController().toLowerCase()) {
            case "ryu":
                manager.getControllerm().doControl("stop ryu");
                break;
            case "floodlight":
                manager.getControllerm().doControl("stop floodlight");
                break;
            case "odl":
                manager.getControllerm().doControl("stop odl");
                break;
            case "onos":
                manager.getControllerm().doControl("stop onos");
                break;
            case "pox":
                manager.getControllerm().doControl("stop pox");
                break;
        }
    }
}

